package com.qianfeng.openapi.search.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.qianfeng.openapi.search.service.SearchService;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.metrics.avg.Avg;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Description:
 * @Company: 千锋互联
 * @Author: 李丽婷
 * @Date: 2020/12/29
 * @Time: 上午10:37
 */
@Service
public class SearchServiceImpl implements SearchService {

    @Value("${elasticsearch.index.name}")
    private String  index;

    @Value("${elasticsearch.type.name}")
    private String  type;

    @Resource
    private RestHighLevelClient restHighLevelClient;//config.ElasticSearchConfig 类下构建的

    private Logger logger = LoggerFactory.getLogger(SearchServiceImpl.class);

    @Override
    public boolean createIndex() {
        try {
            //1.请求对象
            CreateIndexRequest createIndexRequest = new CreateIndexRequest();
            createIndexRequest.index(index);

            //2.设置索引的配置  分片  从
            //"settings": {
            //    "number_of_shards": 3,
            //    "number_of_replicas": 1
            //  }
            Settings.Builder settings = Settings.builder().put("number_of_shards",3)
                    .put("number_of_replicas",1);
            createIndexRequest.settings(settings);

            //3.设置索引的类型和映射信息（表和字段）
//        "properties":{
//            "id":{
//                   "type":"integer",
//                        "index":true,
//                        "store":true
//            },
            XContentBuilder mappings = JsonXContent.
                    contentBuilder()
                    .startObject() //{
                    .startObject("properties") // "properties":{
                    .startObject("appKey") //"id":{
                    .field("type","keyword").
                            field("index",true)
                    .endObject()  //}
                    .startObject("servIP") //存储ip  123.123.122.111
                    .field("type","ip")
                    //ik_smart
                    .endObject()
                    .startObject("venderId")
                    .field("type","long")
                    .endObject()
                    .startObject("remoteIp")
                    .field("type","ip")
                    .endObject()
                    .startObject("apiName")
                    .field("type","keyword")
                    //text:分词  keyword：聚合查询    分组统计
                    .endObject()
                    .startObject("platformRepTime")
                    .field("type","long")
                    .endObject()
                    .startObject("requestContent")
                    .field("type","text")
                    .field("analyzer","ik_smart")//
                    .endObject()
                    .startObject("errorCode")
                    .field("type","text")
                    .endObject()
                    .startObject("receiveTime")
                    .field("type","long")
                    .endObject()
                    .startObject("createTime")
                    .field("type","date")
                    .field("format","yyyy-mm-dd HH:mm:ss")
                    .endObject()

                    .endObject()
                    .endObject();

            //文本类型：  text(分词，不支持聚合查询)|keyword（不分词，聚合查询）
            createIndexRequest.mapping(type,mappings);

            //4.创建索引
            AcknowledgedResponse response =  restHighLevelClient.indices().create(createIndexRequest,RequestOptions.DEFAULT);

            logger.info("{创建索引：}"+response);
            return  response.isAcknowledged();
        } catch (IOException e) {
            e.printStackTrace();
            logger.info("{创建索引异常}"+e.getMessage());
        }
        logger.info("{创建索引：}失败");
        return  false;
    }

    @Override
    public boolean delIndex() {
        try {
            //1,删除索引的请求对象
            DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest();
            deleteIndexRequest.indices(index);
            //删除
            AcknowledgedResponse f =   restHighLevelClient.indices().delete(deleteIndexRequest,RequestOptions.DEFAULT);
           return f.isAcknowledged();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return false;
    }

    @Override
    public void addData(String json) {
        try {
            //1.请求对象
            IndexRequest indexRequest = new IndexRequest(index,type);
            indexRequest.source(json,XContentType.JSON);
            //2.新增
            IndexResponse response = restHighLevelClient.index(indexRequest,RequestOptions.DEFAULT);

            logger.info("{新增数据}"+response.status());

            return;

        } catch (IOException e) {
            e.printStackTrace();
            logger.info("{新增数据异常}"+e.getMessage());
        }
        logger.info("{新增数据失败}");

    }

    /**
     * 求api平均调用时间  求每个作者出版图书的平均价格
     * apiName:jingdong.product.info      jingdong.product.delete
     * platformRepTime  调用api花费的时间
     * @param startTime
     * @param endTime
     * @return
     * @throws IOException
     */
    @Override
    public Map<String, Integer> statAvg(long startTime, long endTime) throws IOException {
       //1,查询请求对象
        SearchRequest searchRequest = new SearchRequest(index);
        searchRequest.types(type);
        //2,构建查询类型构造器
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //3,指定查询条件
        searchSourceBuilder.query
                (QueryBuilders.rangeQuery("receiveTime").gte(startTime).lte(endTime));
        //4，聚合查询
          //1)先按照api分组
        /**
         * "aggs": {
         *     "group_author": {//1)
         *       "terms": {
         *         "field": "apiName"
         *       }
         *       ,"aggs": {  2)
         *         "avg_price": {
         *           "avg": {
         *             "field": "platformRepTime"
         *           }
         *         }
         *       }
         */
      AggregationBuilder aggregationBuilder =  AggregationBuilders.terms("group_apiName").field("apiName");
      //2)分组后求平均值
       aggregationBuilder.subAggregation(AggregationBuilders.avg("avg_time").field("platformRepTime"));//子聚合
       searchSourceBuilder.aggregation(aggregationBuilder);

       searchRequest.source(searchSourceBuilder);
      //5. 执行查询  得到结果
       SearchResponse response = restHighLevelClient.search(searchRequest,RequestOptions.DEFAULT);
        /**
         * "aggregations" : {
         *     "group_apiName" : {
         *       "doc_count_error_upper_bound" : 0,
         *       "sum_other_doc_count" : 0,
         *       "buckets" : [
         *         {
         *           "key" : "jingdong.product.info",
         *           "doc_count" : 5,
         *           "avg_time" : {
         *             "value" : 34.0
         *           }
         *         }, {
         *           "key" : "jingdong.product.delete",
         *           "doc_count" : 1,
         *           "avg_price" : {
         *             "value" : 34.0
         *           }}]}
         */
        Aggregations aggregations = response.getAggregations();
        Terms  group =   aggregations.get("group_apiName");
        List<? extends Terms.Bucket> list = group.getBuckets();
        Map<String, Integer> map = new HashMap<>();
        for (Terms.Bucket bucket : list) {
            String  key = bucket.getKey()+"";
            Avg avg = bucket.getAggregations().get("avg_time");
            Double d = avg.getValue();
            Integer avgTime = d.intValue();

            logger.info("{api平均调用时间：}"+key+"---"+avgTime);
            map.put(key,avgTime);
        }


        return map;
    }

    /**
     * 统计满足条件结果条数
     * @param params
     * @return
     * @throws IOException
     */
    @Override
    public long count(String params) throws IOException {
        return  search(params).size();
    }

    /**
     * 分页+模糊搜索日志
     * @param searchParams  搜索的关键字组装json   {"requestContent":"海尔","startTime":10,"endTime":100000}
     * @return
     * @throws IOException
     */
    @Override
    public List<Map> search(String searchParams) throws IOException {
        try {
            //1.创建查询请求对象
            SearchRequest searchRequest = new SearchRequest(index);
            searchRequest.types(type);//指定类型

            //2.构建查询类型
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();

//          //{"requestContent":"海尔","startTime":10,"endTime":100000,start:10,size:10}
            //根据 搜索日志关键字  查询日志
            JSONObject jsonObject = JSONObject.parseObject(searchParams);
            String requestContent = jsonObject.getString("requestContent");
            Long startTime = jsonObject.getLong("startTime");
            Long endTime = jsonObject.getLong("endTime");
            Integer start = jsonObject.getInteger("start");
            Integer size = jsonObject.getInteger("size");

            if (StringUtils.isNotBlank(requestContent)){
                boolQueryBuilder.must(QueryBuilders.matchQuery("requestContent",requestContent));
            }

            if (startTime!=null&&endTime!=null){
                boolQueryBuilder.must(QueryBuilders.rangeQuery("receiveTime").gte(startTime).lte(endTime));
            }
            if (startTime==null&&endTime!=null){
                boolQueryBuilder.must(QueryBuilders.rangeQuery("receiveTime").lte(endTime));
            }

            if (startTime!=null&&endTime==null){
                boolQueryBuilder.must(QueryBuilders.rangeQuery("receiveTime").gte(startTime));
            }

            if (start!=null&&size!=null){
                searchSourceBuilder.from(start);
                searchSourceBuilder.size(size);
            }



            //  高亮部分
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            highlightBuilder.field("requestContent")
                    .preTags("<font color=\"red\">")
                    .postTags("</font>");

            searchSourceBuilder.highlighter(highlightBuilder);

            searchSourceBuilder.query(boolQueryBuilder);
            searchRequest.source(searchSourceBuilder);//指定查询类型

            //3.执行请求
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

            //
            SearchHits searchHits = searchResponse.getHits();

            SearchHit searchHit[] = searchHits.getHits();
            List<Map> list = new ArrayList<>();
            for (SearchHit hit : searchHit) {
               // System.out.println(hit.getSourceAsMap());
                Map<String,Object> sourceAsMap = hit.getSourceAsMap();
                logger.info("{resultMap}"+sourceAsMap);
                Map<String, HighlightField> map = hit.getHighlightFields();
                //logger.info("{HighlightField}--"+map);
                //得到高亮部分的结果
                HighlightField highlightField = map.get("requestContent");
                Text[] texts = highlightField.getFragments();
                if (texts!=null&&texts.length>0){
                    Text t = texts[0];//<font color="red">海尔</font>冰箱
                    logger.info("{HighlightField}--"+t);
                    sourceAsMap.put("requestContent",t.toString());
                }
                //requestContent=海尔冰箱--->requestContent = <font color="red">海尔</font>冰箱

                list.add(sourceAsMap);
            }

            return  list;
        } catch (IOException e) {
            e.printStackTrace();
        }

        return  null;

    }

}


